﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml;
using AvalonDock;
using Meebey.SmartIrc4net;
using Microsoft.Windows.Controls.Ribbon;
using Thunder.Irc.Formatter;
using Thunder.Irc.Parser;
using Thunder.IrcClient.Properties;

namespace Thunder.IrcClient
{
    /// <summary>
    /// This enumeration contains the possible connection states
    /// </summary>
    public enum ConnectionState
    {
        /// <summary>
        /// Connecting to the server
        /// </summary>
        Connecting,

        /// <summary>
        /// Connected to the server
        /// </summary>
        Connected,

        /// <summary>
        /// Disconnecting from the server
        /// </summary>
        Disconnecting,

        /// <summary>
        /// Disconnected from the server
        /// </summary>
        Disconnected
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        private static readonly DependencyProperty ConnectionStateProperty = DependencyProperty.Register("ConnectionState", typeof(ConnectionState), typeof(MainWindow));

        private static readonly string DockPanelLayoutFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunder Irc\\DockPane.xml";
        private static readonly DependencyProperty IsDisconnectedProperty = DependencyProperty.Register("IsDisconnected", typeof(bool), typeof(MainWindow));
        private readonly Dictionary<string, ChannelControl> channelMapping = new Dictionary<string, ChannelControl>();
        private readonly SortableObservableCollection<string> channels = new SortableObservableCollection<string>();

        private readonly Meebey.SmartIrc4net.IrcClient client = new Meebey.SmartIrc4net.IrcClient();

        private readonly BackgroundWorker clientWorker = new BackgroundWorker();

        private readonly FormatManager formatManager;
        private readonly ParserManager parserManager;

        private string currentChannel;

        /// <summary>
        /// Gets the current connection state
        /// </summary>
        public ConnectionState ConnectionState
        {
            get { return (ConnectionState)this.GetValue(ConnectionStateProperty); }
            private set { this.SetValue(ConnectionStateProperty, value); }
        }

        /// <summary>
        /// Indicates if it's possible to send a command to a server
        /// </summary>
        public bool IsDisconnected
        {
            get { return (bool)this.GetValue(IsDisconnectedProperty); }
            private set { this.SetValue(IsDisconnectedProperty, value); }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public MainWindow()
        {
            this.parserManager = new ParserManager(this.client);
            this.formatManager = new FormatManager(this.client);

            Application.Current.DispatcherUnhandledException += this.Application_DispatcherUnhandledException;
        }

        private void Application_Close(object sender, ExecutedRoutedEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Handles the <see cref="Application.DispatcherUnhandledException"/> event
        /// </summary>
        /// <param name="sender">The <see cref="Application"/> raising the event</param>
        /// <param name="e">The event arguments for the event</param>
        /// <remarks>This method tries to write a file, if any exception arises during this the original exception is rethrown</remarks>
        private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            try
            {
                using (XmlTextWriter log = new XmlTextWriter("UnhandledException.log", Encoding.UTF8))
                {
                    log.WriteStartElement("log");

                    log.WriteStartElement("exception");
                    LogEntry(log, "message", e.Exception.Message);
                    LogEntry(log, "stacktrace", e.Exception.StackTrace);
                    log.WriteEndElement();

                    log.WriteStartElement("connection");
                    if (this.client.IsConnected == true)
                    {
                        LogEntry(log, "IsConnected", this.client.IsConnected);

                        this.clientWorker.CancelAsync();
                        this.client.Disconnect();
                    }
                    log.WriteEndElement();

                    log.WriteStartElement("channels");
                    foreach (ChannelControl control in this.channelMapping.Values)
                    {
                        log.WriteStartElement("channel");
                        LogEntry(log, "name", control.Title);
                        TextRange textRange = new TextRange(control.ChannelText.Document.ContentStart, control.ChannelText.Document.ContentEnd);
                        LogEntry(log, "messages", textRange.Text);
                        log.WriteEndElement();
                    }
                    log.WriteEndElement();

                    log.WriteEndElement();
                }
            }
            catch
            {
                throw e.Exception;
            }
        }

        private void ChangeThemeBlack_Execute(object sender, ExecutedRoutedEventArgs e)
        {
            this.RemoveThemes();

            this.Resources.MergedDictionaries.Add(PopularApplicationSkins.Office2007Black);
        }

        private void ChangeThemeBlue_Execute(object sender, ExecutedRoutedEventArgs e)
        {
            this.RemoveThemes();

            this.Resources.MergedDictionaries.Add(PopularApplicationSkins.Office2007Blue);
        }

        private void ChangeThemeSilver_Execute(object sender, ExecutedRoutedEventArgs e)
        {
            this.RemoveThemes();

            this.Resources.MergedDictionaries.Add(PopularApplicationSkins.Office2007Silver);
        }

        private void Client_Connected(object sender, EventArgs e)
        {
            Settings.Default.QuickPort = this.client.Port;
            Settings.Default.QuickServer = this.client.Address;
            Settings.Default.Save();

            this.Dispatcher.Invoke((MethodInvoker)(() => this.UpdateUIState(ConnectionState.Connected)));
        }

        private void Client_Connecting(object sender, EventArgs e)
        {
            this.Dispatcher.Invoke((MethodInvoker)(() => this.UpdateUIState(ConnectionState.Connecting)));
        }

        private void Client_Disconnected(object sender, EventArgs e)
        {
            this.Dispatcher.Invoke((MethodInvoker)(() => this.UpdateUIState(ConnectionState.Disconnected)));
        }

        private void Client_Disconnecting(object sender, EventArgs e)
        {
            this.Dispatcher.Invoke((MethodInvoker)(() => this.UpdateUIState(ConnectionState.Disconnecting)));
        }

        private static void Client_NickChange(object sender, NickChangeEventArgs e)
        {
            Settings.Default.Nick = e.NewNickname;
        }

        private void Client_RawMessage(object sender, IrcEventArgs e)
        {
            Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "[{0}] - Channel: {1} - From: {2} -> {3}", e.Data.Type, e.Data.Channel, e.Data.Nick, e.Data.Message));
            this.clientWorker.ReportProgress(0, e.Data);
        }

        private void ClientWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            ConnectionInfo connectionInfo = e.Argument as ConnectionInfo;
            if (connectionInfo == null)
            {
                throw new ArgumentException("Worker arguments must be a ConnectionInfo");
            }

            this.Connect(connectionInfo);

            this.client.Login(new[] { Settings.Default.Nick }, Settings.Default.RealName);

            while (this.client.IsConnected == true && this.clientWorker.CancellationPending == false)
            {
                Thread.Sleep(10);
                if (this.client.IsConnected == false || this.clientWorker.CancellationPending == true)
                {
                    return;
                }

                try
                {
                    this.client.ListenOnce(false);
                }
                catch (Exception exception)
                {
                    using (StreamWriter sw = new StreamWriter("Exception.log"))
                    {
                        sw.WriteLine(exception);
                    }
                }
            }
        }

        /// <summary>
        /// This method tries to create a connection with the specified <see cref="ConnectionInfo"/> values
        /// </summary>
        /// <param name="connectionInfo">The connection information</param>
        private void Connect(ConnectionInfo connectionInfo)
        {
            try
            {
                this.client.Connect(connectionInfo.Address, connectionInfo.Port);
            }
            catch (CouldNotConnectException exception)
            {
                MessageBox.Show(this, exception.Message, Localized.DialogCouldNotConnect);
            }
        }

        private void ClientWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            IrcMessageData data = e.UserState as IrcMessageData;
            if (data == null)
            {
                return;
            }

            if (data.Type == ReceiveType.List)
            {
                this.ParseList(data);
            }
            else
            {
                this.parserManager.ParseMessage(data);
            }
        }

        private void ClientWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.ParserManager_TextAdded(string.Empty, new IrcMessageData(null, "#System", "#System", string.Empty, string.Empty, string.Empty, Localized.ServerDisconnected, Localized.ServerDisconnected, ReceiveType.Unknown, ReplyCode.Null), Localized.ServerDisconnected);
        }

        private void ConnectServer_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.ValidateButtonState(e.Command);
        }

        private void ConnectServer_Executed(object sender, ExecutedRoutedEventArgs e)
        {
        }

        private void Disconnect_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.ValidateButtonState(e.Command);
        }

        private void Disconnect_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.client.IsConnected == false)
            {
                if (MessageBox.Show(this, Localized.DialogDisconnect, NonLocalized.DialogCaption, MessageBoxButton.YesNo) == MessageBoxResult.No)
                {
                    return;
                }
            }

            foreach (string channel in this.client.JoinedChannels)
            {
                this.client.RfcPart(channel);
            }

            this.clientWorker.CancelAsync();
            this.client.Disconnect();
        }

        private void DockingManager_Loaded(object sender, RoutedEventArgs e)
        {
            if (File.Exists(DockPanelLayoutFile) == true)
            {
                this.dockManager.RestoreLayout(DockPanelLayoutFile);
            }
        }

        private void DockingManager_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "ActiveDocument":
                    this.currentChannel = this.dockManager.ActiveDocument.Title;
                    if (this.currentChannel != "Status")
                    {
                        Settings.Default.QuickChannel = this.currentChannel;
                    }

                    this.MemberList.ItemsSource = this.channelMapping.ContainsKey(this.currentChannel) == true ? this.channelMapping[this.currentChannel].ChannelMembers : null;
                    break;
            }
        }

        private MessageInfo FormatMessage(IrcMessageData data, string message)
        {
            message = PreFormatMessage(message);

            MessageInfo messageInfo = new MessageInfo(this.formatManager.FormatMessage(data, message), this.formatManager.GetImagePath(data));

            return messageInfo;
        }

        private static string PreFormatMessage(string message)
        {
            // The RichTextBox can not show control characters (chars less than 32)
            foreach (char c in message)
            {
                if (c < 32)
                {
                    message = message.Replace(c.ToString(), "");
                }
            }

            // Convert the following as these have special meaning in xml
            message = message.Replace("&", "&amp;");
            message = message.Replace("<", "&lt;");
            message = message.Replace(">", "&gt;");

            return message;
        }

        private void GetChannelList_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.client.IsConnected == true;
        }

        private void GetChannelList_Execute(object sender, ExecutedRoutedEventArgs e)
        {
            this.client.WriteLine(Rfc2812.List());
        }

        /// <summary>
        /// Initializes the <see cref="BackgroundWorker"/>
        /// </summary>
        private void InitializeBackgroundWorker()
        {
            this.clientWorker.WorkerReportsProgress = true;
            this.clientWorker.WorkerSupportsCancellation = true;

            this.clientWorker.RunWorkerCompleted += this.ClientWorker_RunWorkerCompleted;
            this.clientWorker.DoWork += this.ClientWorker_DoWork;
            this.clientWorker.ProgressChanged += this.ClientWorker_ProgressChanged;
        }

        /// <summary>
        /// Initializes the <see cref="IrcClient"/>
        /// </summary>
        private void InitializeIrcClient()
        {
            this.client.OnConnecting += this.Client_Connecting;
            this.client.OnConnected += this.Client_Connected;
            this.client.OnDisconnecting += this.Client_Disconnecting;
            this.client.OnDisconnected += this.Client_Disconnected;
            this.client.OnRawMessage += this.Client_RawMessage;

            this.client.OnNickChange += Client_NickChange;
        }

        /// <summary>
        /// Initializes the <see cref="ParserManager"/>
        /// </summary>
        private void InitializeParserManager()
        {
            this.parserManager.ChannelAdded += this.ParserManager_ChannelAdded;
            this.parserManager.ChannelOpened += this.ParserManager_ChannelOpened;
            this.parserManager.ChannelClosed += this.ParserManager_ChannelClosed;

            this.parserManager.TextAdded += this.ParserManager_TextAdded;

            this.parserManager.MemberAdded += this.ParserManager_MemberAdded;
            this.parserManager.MemberRemoved += this.ParserManager_MemberRemoved;

            this.parserManager.ShowJoinPart = Settings.Default.ShowJoinPart;
        }

        private void InputText_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Enter:
                    // Disregard empty texts
                    if (string.IsNullOrEmpty(this.InputText.Text.Trim()) == true)
                    {
                        return;
                    }

                    if (this.InputText.Text.StartsWith("/") == false)
                    {
                        this.SendMessage(this.InputText.Text);
                    }
                    else
                    {
                        if (this.InputText.Text.StartsWith("/connect") == true)
                        {
                            string[] connectString;
                            if (this.ConnectionState == ConnectionState.Connected || this.ConnectionState == ConnectionState.Connecting)
                            {
                                if (MessageBox.Show(this, "Disconnect?", NonLocalized.DialogCaption, MessageBoxButton.YesNo) == MessageBoxResult.No)
                                {
                                    return;
                                }

                                connectString = this.InputText.Text.Split(" ".ToCharArray());
                                this.clientWorker.CancelAsync();
                                this.client.Disconnect();
                            }
                            else
                            {
                                connectString = this.InputText.Text.Split(" ".ToCharArray());
                            }

                            ConnectionInfo connectionInfo = new ConnectionInfo { Address = connectString[1], Port = Int32.Parse(connectString[2]) };

                            this.clientWorker.RunWorkerAsync(connectionInfo);
                        }
                        else
                        {
                            this.SendCommand(this.InputText.Text);
                        }
                    }
                    this.InputText.Clear();
                    break;
            }
        }

        /// <summary>
        /// Loads the string into the target <see cref="RichTextBox"/>
        /// </summary>
        /// <param name="formattedText">The xaml formatted string to load</param>
        /// <param name="richTextBox">The <see cref="RichTextBox"/> to load the text into</param>
        private static void LoadMessage(string formattedText, RichTextBox richTextBox)
        {
            if (string.IsNullOrEmpty(formattedText))
            {
                throw new ArgumentNullException();
            }

            TextRange textRange = new TextRange(richTextBox.Document.ContentEnd, richTextBox.Document.ContentEnd);

            bool scrollToEnd = IsScrolledToEnd(richTextBox);

            richTextBox.BeginChange();
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (StreamWriter streamWriter = new StreamWriter(memoryStream))
                {
                    streamWriter.Write(formattedText);
                    streamWriter.Flush();
                    memoryStream.Seek(0, SeekOrigin.Begin);

                    textRange.Load(memoryStream, DataFormats.Xaml);
                }
            }
            richTextBox.EndChange();

            if (scrollToEnd == false)
            {
                return;
            }

            richTextBox.ScrollToEnd();
            richTextBox.UpdateLayout();
        }

        /// <summary>
        /// This method determines if a <see cref="RichTextBox"/> is scrolled to the end
        /// </summary>
        /// <param name="richTextBox">The <see cref="RichTextBox"/> to check</param>
        /// <returns>true if the <see cref="RichTextBox"/> is scrolled to the end, otherwise false</returns>
        private static bool IsScrolledToEnd(TextBoxBase richTextBox)
        {
            // Get the vertical scroll position
            double verticalOffset = richTextBox.VerticalOffset;

            // Get the vertical size of the scrollable content area
            double viewportHeight = richTextBox.ViewportHeight;

            // Get the vertical size of the visible content area
            double extentHeight = richTextBox.ExtentHeight;

            return verticalOffset != 0 && (verticalOffset + viewportHeight) == extentHeight;
        }

        private static void LogEntry(XmlWriter log, string element, object value)
        {
            log.WriteStartElement(element);
            log.WriteValue(value);
            log.WriteEndElement();
        }

        private void MainWindow_Closing(object sender, CancelEventArgs e)
        {
            this.dockManager.SaveLayout(DockPanelLayoutFile);

            if (this.client.IsConnected == false)
            {
                return;
            }

            if (MessageBox.Show(Localized.DialogExitProgram, Localized.DialogConnectionOpen, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
            {
                e.Cancel = true;
                return;
            }

            this.client.RfcQuit();
            this.clientWorker.CancelAsync();
            this.client.Disconnect();
        }

        private void MainWindow_Initialized(object sender, EventArgs e)
        {
            this.parserManager.Initialize();
            this.formatManager.Initialize();

            this.ConnectionState = ConnectionState.Disconnected;
            this.IsDisconnected = true;

            this.Resources.MergedDictionaries.Add(PopularApplicationSkins.Office2007Silver);
            this.InitializeIrcClient();
            this.InitializeParserManager();
            this.InitializeBackgroundWorker();

            this.InitializeComponent();

            //this.StatusText.Document = new FlowDocument { PagePadding = new Thickness(0), PageWidth = 0 };
            this.StatusText.MouseDoubleClick += RichTextBox_MouseDoubleClick;
            this.Title = Assembly.GetExecutingAssembly().AssemblyProduct();

            this.dockManager.PropertyChanged += this.DockingManager_PropertyChanged;

            this.ChannelList.ItemsSource = this.channels;

            VerticalScrollHelper.ResetVerticalScroll(this.StatusText, new TextRange(this.StatusText.Document.ContentStart, this.StatusText.Document.ContentEnd).Text);

            this.InputText.Focus();
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            App application = Application.Current as App;
            if (application == null)
            {
                throw new InvalidOperationException("Application must be of type App");
            }

            application.CloseSpashScreen();
        }

        /// <summary>
        /// Parses the data from the /list command
        /// </summary>
        /// <param name="data">The <see cref="IrcMessageData"/> to be parsed</param>
        private void ParseList(IrcMessageData data)
        {
            switch (data.ReplyCode)
            {
                case ReplyCode.ListStart:
                    this.channels.Clear();
                    break;

                case ReplyCode.List:
                    this.ParserManager_ChannelAdded(data.RawMessageArray[3]);
                    break;

                case ReplyCode.ListEnd:
                    MessageBox.Show("Channels: " + this.ChannelList.Items.Count);
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void ParserManager_ChannelAdded(string channel)
        {
            this.channels.Add(channel);
        }

        private void ParserManager_ChannelClosed(string channel)
        {
            if (this.channelMapping.ContainsKey(channel) == true)
            {
                this.channelMapping[channel].Close();
                this.channelMapping.Remove(channel);
            }

            if (this.channels.Contains(channel) == true)
            {
                this.channels.Remove(channel);
            }
        }

        private void ParserManager_ChannelOpened(string channel)
        {
            if (this.channelMapping.ContainsKey(channel) == true)
            {
                return;
            }

            ChannelControl control = new ChannelControl(this.client, channel);
            control.GotFocus += this.RichTextBox_GotFocus;
            control.ChannelText.MouseDoubleClick += RichTextBox_MouseDoubleClick;

            this.channelMapping.Add(channel, control);

            int index = this.documentsHost.Items.Add(control);
            this.documentsHost.SelectedIndex = index;
        }

        private void ParserManager_MemberAdded(string channel, string name)
        {
            SortableObservableCollection<MemberInfo> memberInfos = this.channelMapping[channel].ChannelMembers;
            if (this.channelMapping.ContainsKey(channel) == false)
            {
                return;
            }

            if (memberInfos.Contains(name) == false)
            {
                memberInfos.Add(name);
                memberInfos.Sort();
            }
        }

        private void ParserManager_MemberRemoved(string channel, string name)
        {
            if (string.IsNullOrEmpty(channel) == true)
            {
                foreach (ChannelControl channelControl in this.channelMapping.Values)
                {
                    if (channelControl.ChannelMembers.Contains(name) != true)
                    {
                        continue;
                    }

                    channelControl.ChannelMembers.Remove(name);

                    if (Settings.Default.ShowJoinPart == true)
                    {
                        this.ParserManager_TextAdded(channelControl.ChannelName, new IrcMessageData(this.client, string.Empty, string.Empty, string.Empty, string.Empty, channelControl.ChannelName, string.Empty, string.Empty, ReceiveType.Part, ReplyCode.Null), string.Format(CultureInfo.CurrentCulture, "'{0}' left the channel", name));
                    }
                }
            }
            else
            {
                if (this.channelMapping.ContainsKey(channel) == false)
                {
                    this.ParserManager_ChannelOpened(channel);
                }

                this.channelMapping[channel].ChannelMembers.Remove(name);
            }
        }


        private void ParserManager_TextAdded(string channel, IrcMessageData data, string message)
        {
            DocumentContent content = this.documentsHost.SelectedItem as DocumentContent;
            if (content == null)
            {
                return;
            }

            RichTextBox richTextBox;
            if (string.IsNullOrEmpty(channel) == true)
            {
                richTextBox = this.StatusText;
            }
            else
            {
                if (this.channelMapping.ContainsKey(channel) == false)
                {
                    this.ParserManager_ChannelOpened(channel);
                }

                richTextBox = this.channelMapping[channel].ChannelText;
            }

            string nick = data.Nick;

            Regex regex = new Regex(string.Format(CultureInfo.InvariantCulture, @"(?i)[@+]?(\b{0}\b)", this.client.Nickname));
            Match match = regex.Match(message);

            if (match.Success == true)
            {
                //message = message.Replace(this.client.Nickname, string.Format(CultureInfo.InvariantCulture, @"{{\b {0}\b0}}", this.client.Nickname));
                nick = string.Format(CultureInfo.InvariantCulture, @"<Span Foreground=""#FF0000FF""><Underline>{0}</Underline></Span>", nick);
                data = new IrcMessageData(data.Irc, data.From, nick, data.Ident, data.Host, data.Channel, data.Message, data.RawMessage, data.Type, data.ReplyCode);
            }

            MessageInfo messageInfo = this.FormatMessage(data, message);

            const string formatString = @"<Section xml:space=""preserve"" xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""><Paragraph Margin=""0,0,0,0"">[{0}] <Image Stretch=""Fill"" Margin=""0,0,0,0"" Source=""{1}"" Width=""12"" Height=""12""></Image> {2}</Paragraph></Section>";
            LoadMessage(string.Format(CultureInfo.CurrentCulture, formatString, DateTime.Now.ToLongTimeString(), messageInfo.ImagePath, messageInfo.Message), richTextBox);

            VerticalScrollHelper.ResetVerticalScroll(richTextBox, message);
        }

        private void QuickConnect_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.ValidateButtonState(e.Command) && this.client.IsConnected == false;
        }

        private void QuickConnect_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Debug.Assert(this.client.IsConnected == false, "Client should not be connected when invoking Quick Connect!");

            ConnectionInfo connectionInfo = new ConnectionInfo { Address = Settings.Default.QuickServer, Port = Settings.Default.QuickPort };
            this.clientWorker.RunWorkerAsync(connectionInfo);

            this.currentChannel = "#ThunderTest";
            this.client.RfcJoin(this.currentChannel);
        }

        /// <summary>
        /// Removes all themes from the office control
        /// </summary>
        private void RemoveThemes()
        {
            this.Resources.MergedDictionaries.Remove(PopularApplicationSkins.Office2007Blue);
            this.Resources.MergedDictionaries.Remove(PopularApplicationSkins.Office2007Black);
            this.Resources.MergedDictionaries.Remove(PopularApplicationSkins.Office2007Silver);
        }

        private void RichTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            this.InputText.Focus();
        }

        private static void RichTextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            RichTextBox richTextBox = sender as RichTextBox;
            if (richTextBox == null)
            {
                return;
            }

            TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);

            // sniff out what data format you've got
            string dataFormat = DataFormats.Text;
            string ext = Path.GetExtension("dump.xaml");
            if (String.Compare(ext, ".xaml", true) == 0)
            {
                dataFormat = DataFormats.Xaml;
            }
            else if (String.Compare(ext, ".rtf", true) == 0)
            {
                dataFormat = DataFormats.Rtf;
            }

            if (File.Exists("dump.xml") == true)
            {
                File.Delete("dump.xml");
            }

            using (FileStream stream = File.OpenWrite("dump.xml"))
            {
                documentTextRange.Save(stream, dataFormat);
            }

            MessageBox.Show("Saved");
        }

        private void SendCommand(string text)
        {
            Regex regex = new Regex("^(/me|/action)");

            string message;
            if (regex.Match(text).Success == true)
            {
                message = regex.Replace(text, "");
                message = message.TrimStart(" ".ToCharArray());
                this.client.SendMessage(SendType.Action, this.currentChannel, message);

                // Before writing the action to the ui it has to be prefixed with "ACTION" or the parser will fail
                message = string.Format(CultureInfo.InvariantCulture, "ACTION {0}", message);
                this.ParserManager_TextAdded(this.currentChannel, new IrcMessageData(this.client, string.Empty, Settings.Default.Nick, string.Empty, string.Empty, this.currentChannel, message, string.Empty, ReceiveType.ChannelAction, ReplyCode.Null), message);
            }
            else
            {
                message = text.TrimStart("/".ToCharArray());
                this.client.WriteLine(message);
            }
        }

        /// <summary>
        /// Sends a message to the Irc Server
        /// </summary>
        /// <param name="message">The plain text message to send</param>
        private void SendMessage(string message)
        {
            this.client.SendMessage(SendType.Message, this.currentChannel, message);
            this.parserManager.ParseMessage(new IrcMessageData(this.client, this.client.Realname, this.client.Nickname, this.client.Nickname, string.Empty, this.currentChannel, message, message, ReceiveType.ChannelMessage, ReplyCode.Null));
        }

        private void ShowOptions_Execute(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Bingo");
        }

        /// <summary>
        /// Updates the UI Control states depending on the <paramref name="connectionState"/>
        /// </summary>
        /// <param name="connectionState">The connection state to update the control to</param>
        private void UpdateUIState(ConnectionState connectionState)
        {
            this.ConnectionState = connectionState;

            switch (connectionState)
            {
                case ConnectionState.Connecting:
                    break;
                case ConnectionState.Connected:
                    this.IsDisconnected = false;
                    break;
                case ConnectionState.Disconnecting:
                    break;
                case ConnectionState.Disconnected:
                    this.InputText.Text = string.Empty;
                    this.IsDisconnected = true;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("connectionState");
            }
        }

        /// <summary>
        /// Validates the state of the connection buttons
        /// </summary>
        /// <param name="command">The <see cref="ICommand"/> for the button to verify</param>
        /// <returns>Returns true if the is invokeable, otherwise false</returns>
        private bool ValidateButtonState(ICommand command)
        {
            RibbonCommand ribbonCommand = command as RibbonCommand;
            if (ribbonCommand == null)
            {
                return true;
            }

            bool result = false;

            switch (ribbonCommand.CommandName)
            {
                case "ConnectServer":
                case "QuickConnect":
                    result = this.ConnectionState == ConnectionState.Disconnected;
                    break;

                case "Disconnect":
                    result = this.ConnectionState == ConnectionState.Connected;
                    break;
            }

            return result;
        }

        /// <summary>
        /// Delegate used for calling methods cross threads
        /// </summary>
        private delegate void MethodInvoker();
    }
}